/**
 * \file dmxcon/drivers/dmx4all.c
 * \author Christian Spoo
 * \date 02.08.2010
 *
 * DMX4ALL device driver
 */

#include <errno.h>
#include <fcntl.h>
#include <malloc.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>

#include <dmxcon/device.h>
#include <dmxcon/drivers/dmx4all.h>

struct device_t *dmx4all_create(const char *filename) {
  struct termios options;

  /* Create device structure */
  struct device_t *device = device_create(); 

  if (!device)
    return NULL;

  /* Copy device filename */
  device->devfile = strdup(filename);

  /* Setup device operations */
  device->ops.destroy = dmx4all_destroy;
  device->ops.refresh = dmx4all_refresh;
  device->ops.set_range = dmx4all_set_range;
  device->ops.set_channel = dmx4all_set_channel;

  /* Open serial device */
  device->ctrl_fd = open(device->devfile, O_RDWR | O_NOCTTY | O_NDELAY | O_SYNC);
  if (device->ctrl_fd < 0) {
    device_destroy(device);
    return NULL;
  }

  /* Configure serial device */
  tcgetattr(device->ctrl_fd, &options);
  cfsetispeed(&options, B38400);
  cfsetospeed(&options, B38400);
  options.c_cflag |= (CLOCAL | CREAD);
  options.c_cflag &= ~PARENB;
  options.c_cflag &= ~CSTOPB;
  options.c_cflag &= ~CSIZE;
  options.c_cflag |= CS8;
  tcsetattr(device->ctrl_fd, TCSANOW, &options);

  /* Initialize adapter */
  dmx4all_write_command(device, "C?");

  return device;
}

int dmx4all_destroy(struct device_t *device) {
  if (device) {
    /* Close file descriptor */
    if (device->ctrl_fd >= 0)
      close(device->ctrl_fd);

    /* Free device filename string */
    if (device->devfile) {
      free(device->devfile);
      device->devfile = NULL;
    }

    /* Free device structure */
    free(device);
  }

  return 0;
}

int dmx4all_write_command(struct device_t *device, const char *command) {
  if (device)
    return write(device->ctrl_fd, command, strlen(command));
  else
    return -1;
}

int dmx4all_set_range(struct device_t *device, unsigned short end_addr) {
  char command[5];

  if (end_addr > 511) {
    errno = EINVAL;
    return -1;
  }

  sprintf(command, "N%03u", end_addr);
  if (dmx4all_write_command(device, command) < 0)
    return -1;
  else
    return 0;
}

int dmx4all_set_channel(struct device_t *device, unsigned short channel, unsigned char value) {
  char command[9];

  if (channel > 511) {
    errno = EINVAL;
    return -1;
  }

  printf("dmx4all: Setting channel %03u to value %03u\n", channel, value);

  sprintf(command, "C%03uL%03u", channel, value);
  if (dmx4all_write_command(device, command) < 0)
    return -1;
  else
    return 0;
}

int dmx4all_refresh(struct device_t *device) {
  /*! \todo Device refresh code */

  return 0;
}

